using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;

namespace Pinwheel.Toolkits
{
    public interface IObjectPoolInst<P>
    {
        int Priority { get; }
        void Free(P param, bool ignoreValid = false);  
    }
    
    public partial class ObjectPoolInst<T, P> : IObjectPoolInst<P>
    {
        public class ObjectPoolItemRef
        {
            int m_Index = -1;
            int m_Valid = -1;
            ObjectPoolItemType m_ItemType = ObjectPoolItemType.Temporary;
            public T Item;
            
            public int InstId => m_Index;
            public int Valid => m_Valid;
            public ObjectPoolItemType ItemType => m_ItemType;
            public bool IsIdle => m_Valid < 0 || ObjectPoolUtil.GetTickCountDiff(ObjectPoolUtil.GetTickCount(), m_Valid) >= 0;

            static ObjectPoolItemRef()
            {
                ObjectPoolInst<T, P>.SetInstId = SetInstId;
                ObjectPoolInst<T, P>.SetValid = SetValid;
                ObjectPoolInst<T, P>.SetItemType = SetItemType;
            }
            
            public ObjectPoolItemRef(int instId, int valid, T item, ObjectPoolItemType itemType = ObjectPoolItemType.Persistent)
            {
                m_Index = instId;
                m_Valid = valid;
                Item = item;
                m_ItemType = itemType;
            }
            
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public void UpdateValid(int valid) => m_Valid = ObjectPoolUtil.GetTickCount(valid);


            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public void SetInvalid()
            {
                // Log.Error($"PoolItem SetInvalid Item: {Item}");
                if (Lookup.ContainsKey(Item)) Lookup.Remove(Item);
                m_ItemType = ObjectPoolItemType.Initialization;
            }
            
            private static void SetInstId(ObjectPoolItemRef item, int instId) => item.m_Index = instId;
            private static void SetValid(ObjectPoolItemRef item, int valid) => item.m_Valid = valid;
            private static void SetItemType(ObjectPoolItemRef item, ObjectPoolItemType itemType) => item.m_ItemType = itemType;
        }
        
        public struct ObjectPoolDisposeItem
        {
            public ObjectPoolItemRef ItemRef;
            public ObjectPoolInst<T, P> PoolInst;
            public ObjectDispose Release;
            public P param;
        }

        private ObjectPoolItemRef[][] m_Items = new ObjectPoolItemRef[2][]; // 0: Temporary, 1: Persistent
        private int[] m_Indexs = new int[2];
        private int[] m_Counts = new int[2];
        // private T[] m_Items;
        // private int[] m_ItemTicks;

        private int m_PoolIndex;
        private int m_MaxCount = MaxCount;
        // private int m_Index;
        // private int m_Total;
        private bool m_Debug = false;


        public int TickCount = REF_IDLE;
        public int Priority => m_PoolIndex;
        public bool IsIdle => TickCount < 0 || ObjectPoolUtil.GetTickCountDiff(ObjectPoolUtil.GetTickCount(), TickCount) >= 0;
        
        
        public const int InitCount = 2;
        public const int MaxCount = 64;
        
        public const int StepCount = 4;
        public const int ValidTick = 16;

        public const int REF_IDLE = -1;
        public const int REF_USED = int.MaxValue;

        public delegate T ObjectCtor(P param);
        public delegate void ObjectDispose(T obj, P param);
        public delegate void SetProperty<TObj, TValue>(TObj obj, TValue value); 

        protected ObjectCtor m_Ctor;
        protected ObjectDispose m_Dispose;
        protected static SetProperty<ObjectPoolItemRef, int> SetInstId;
        protected static SetProperty<ObjectPoolItemRef, int> SetValid;
        protected static SetProperty<ObjectPoolItemRef, ObjectPoolItemType> SetItemType;

        [ThreadStatic] private static bool s_Init;
        [ThreadStatic] private static int s_CheckTempTick;
        [ThreadStatic] private static List<ObjectPoolDisposeItem> m_TemporaryList;
        [ThreadStatic] private static List<ObjectPoolDisposeItem> m_PersistentList;
        
        [ThreadStatic] private static Dictionary<T, ObjectPoolItemRef> m_Lookup;
        public static Dictionary<T, ObjectPoolItemRef> Lookup
        {
            get
            {
                if (m_Lookup == null) 
                    m_Lookup = new Dictionary<T, ObjectPoolItemRef>();
                return m_Lookup;
            }
        }
        
        // Dictionary<T, ObjectPoolItemRef> m_Lookup;
        // public Dictionary<T, ObjectPoolItemRef> Lookup
        // {
        //     get
        //     {
        //         if (m_Lookup == null) 
        //             m_Lookup = new Dictionary<T, ObjectPoolItemRef>();
        //         return m_Lookup;
        //     }
        // }
        
        [ThreadStatic] private static Dictionary<ObjectPoolItemRef, ObjectPoolDisposeItem> m_LookupDispose;
        public static Dictionary<ObjectPoolItemRef, ObjectPoolDisposeItem> LookupDispose
        {
            get
            {
                if (m_LookupDispose == null) 
                    m_LookupDispose = new Dictionary<ObjectPoolItemRef, ObjectPoolDisposeItem>();
                return m_LookupDispose;
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        static void CheckInitObjectPoolInst()
        {
            if (s_Init) return;
            m_TemporaryList = new List<ObjectPoolDisposeItem>();
            m_PersistentList = new List<ObjectPoolDisposeItem>();
            s_Init = true;
        }

        static ObjectPoolInst()
        {
            CheckInitObjectPoolInst();
        }

        public ObjectPoolInst(int poolIndex, int initCount = InitCount)
        {
            m_PoolIndex = poolIndex;
            for (int i = 0; i < m_Items.Length; i++)
            {
                m_Items[i] = new ObjectPoolItemRef[initCount];
            }
            // m_Items = new T[initCount];
            // m_ItemTicks = new int[initCount];
            // m_Total = initCount;
            Reset();
        }
        
        public int GetCount(ObjectPoolItemType itemType) => m_Counts[(int) itemType];
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private ObjectPoolItemRef CreateItemRef(ref P param, in int index, ObjectPoolItemType itemType = ObjectPoolItemType.Persistent, int valid = REF_IDLE)
        {
            return new ObjectPoolItemRef(index, valid, null != m_Ctor ? m_Ctor(param) : default, itemType);
        }
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private ObjectPoolItemRef GetOrCreateItemRef(ref P param, ref ObjectPoolItemRef[] itemRefs, in int index, ObjectPoolItemType itemType = ObjectPoolItemType.Persistent, int valid = -1)
        {
            if (itemRefs[index] == null)
            {
                var itemRef = CreateItemRef(ref param, in index, itemType, valid);
                itemRefs[index] = itemRef;
                try
                {
                    // if (typeof(T) == typeof(Collections.QListPtr<int>))
                    //     Log.Error($"[{m_MaxCount}][{GetCount(itemType)}]add [{itemRef.Item.GetHashCode()}]{itemRef.InstId} to Lookup");
                    Lookup.Add(itemRef.Item, itemRef);
                }
                catch 
                {
                    var hash = itemRef.Item.GetHashCode();
                    Log.Error($"Try to add [{typeof(T)}][{itemRef.InstId}]{hash} to Lookup, but it already exists! exist: [{Lookup[itemRef.Item].Item.GetType()}][{Lookup[itemRef.Item].InstId}]{Lookup[itemRef.Item].Item.GetHashCode()}");
                }
            }
            return itemRefs[index];
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void SwapItemRef(ref ObjectPoolItemRef[] itemRefs, in int source, in int target)
        {
            var tmpItem = itemRefs[target];
            itemRefs[target] = itemRefs[source];
            if (null != SetInstId) SetInstId(itemRefs[target], target);
            itemRefs[source] = tmpItem;
            if (null != SetInstId) SetInstId(itemRefs[source], source);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void CorrectItemRef(ref ObjectPoolItemRef[] itemRefs, in int itemIndex, out ObjectPoolItemRef itemRef, int count = StepCount)
        {
            count = Math.Min(count, m_Items.Length - 1);
            
            var source = itemRefs[itemIndex];
            bool find = false;
            itemRef = source;
            
            for (int i = 0; i < count; i++)
            {
                var index = source.InstId + 1;
                if (index >= m_Items.Length) index = 0;

                var target = itemRefs[index];
                if (null == target || source.Valid < 0) break;
                
                // if (source.Valid <= target.Valid) continue;
                if (!target.IsIdle && ObjectPoolUtil.GetTickCountDiff(source.Valid, target.Valid) <= 0) continue;
                // Log.Error($"[{index}]SwapItemRef [{source.InstId}]source: {source.Valid}, [{target.InstId}]target: {target.Valid}");
                SwapItemRef(ref itemRefs, source.InstId, index);
                source = target;
                
                if (find) continue;
                itemRef = target;
                find = true;
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public ObjectPoolItemRef GetTemporaryItem(ref P param, ref ObjectPoolItemRef[] itemRefs, ref int index, int valid = ValidTick)
        {
            Profiler.Start("ObjectPoolInst GetTemporaryItem");
            if (index >= itemRefs.Length) index = 0;
            
            var tickCount = ObjectPoolUtil.GetTickCount();
            var validCount = ObjectPoolUtil.GetTickCount(valid);
            var itemType = ObjectPoolItemType.Temporary;
            
            ObjectPoolItemRef itemRef = GetOrCreateItemRef(ref param, ref itemRefs, index, itemType);
            // if (tickCount < itemRef.Valid)
            if (!itemRef.IsIdle) CorrectItemRef(ref itemRefs, index, out itemRef);

            // if (typeof(P) == typeof(int) && typeof(T) == typeof(SdGraph))
                // Log.Info($"[{m_MaxCount}][{GetCount(itemType)}]GetTemporaryItem({param}): index->{index}[{itemRefs.Length}], tickCount->[{tickCount}][{itemRef.Valid}][{itemRef.IsIdle}]");
            if (!itemRef.IsIdle)
            {
                if (itemRefs.Length < m_MaxCount)
                {
                    index = itemRefs.Length;
                    ArrayExtensions.CheckArraySize(ref itemRefs, index + 1, m_MaxCount);
                    // ArrayExtensions.CheckArraySize(ref m_ItemTicks, index + 1, MaxCount);
                    itemRef = GetOrCreateItemRef(ref param, ref itemRefs, index, itemType, validCount); 
                }
                else
                {
                    itemRef = null;
                }
            }
            else
            {
                if (null != SetValid) SetValid(itemRef, validCount);
            }

            // Log.Info($"ObjectPoolInst GetItem itemCount: {m_Items.Length}, index: {m_Index}, success: {success}, poolIndex: {m_PoolIndex}, item: {item}");
            Profiler.End();
            return itemRef;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public ObjectPoolItemRef GetPersistentItem(ref P param, ref ObjectPoolItemRef[] itemRefs, ref int index)
        {
            Profiler.Start("ObjectPoolInst GetPersistentItem");
            var itemType = ObjectPoolItemType.Persistent;

            ObjectPoolItemRef itemRef = null;
            if (index >= itemRefs.Length && index < m_MaxCount)
                ArrayExtensions.CheckArraySize(ref itemRefs, index + 1, m_MaxCount);
            if (index >= 0 && index < itemRefs.Length)
                itemRef = GetOrCreateItemRef(ref param, ref itemRefs, index, itemType, REF_USED);
            
            // Log.Error($"GetPersistentItem: index->{index}, length->{itemRefs.Length}");
            
            Profiler.End();
            return itemRef;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public ObjectPoolItemRef GetOutOfRangeItem(ref P param, ObjectPoolItemType itemType = ObjectPoolItemType.Temporary, int valid = REF_IDLE)
        {
            Profiler.Start("ObjectPoolInst GetOutOfRangeItem");
            ObjectPoolItemRef itemRef = CreateTmpItem(ref param, valid);
            if (null != m_Dispose)
            {
                var list = ObjectPoolItemType.Temporary == itemType ? m_TemporaryList : m_PersistentList;
                // if (list.Count >= m_MaxCount + m_MaxCount / 2) Log.Warn($"[{itemType}][{m_MaxCount}][{typeof(T)}]ObjectPoolInst out of range item count[{list.Count}] is too large!");
                var item = new ObjectPoolDisposeItem
                {
                    ItemRef = itemRef,
                    PoolInst = this,
                    Release = m_Dispose,
                    param = param,
                };
                list.Add(item);
                SetInstId(itemRef, list.Count - 1);
                try
                {
                    // if (typeof(T) == typeof(Collections.QListPtr<int>))
                    //     Log.Error($"[{m_MaxCount}][{GetCount(itemType)}]add [{itemRef.Item.GetHashCode()}]{itemRef.InstId} to Lookup");
                    Lookup.Add(itemRef.Item, itemRef);   
                }
                catch
                {
                    var dest = itemRef.Item;
                    var exist = Lookup[itemRef.Item];
                    Log.Error($"Try to add [{typeof(T)}][{itemRef.InstId}]{dest.GetHashCode()} to Lookup, but it already exists! exist: [{exist.GetType()}][{exist.InstId}]{exist.Item.GetHashCode()}");
                }
                LookupDispose.Add(itemRef, item);
            }
#if DEBUG            
            int typeIndex = (int) itemType;
            Log.Error($"ObjectPoolInst<{typeof(T)}, {typeof(P)}> GetOutOfRangeItem instId: [{m_MaxCount}][{m_Counts[typeIndex]}][{m_Indexs[typeIndex]}]{itemRef.InstId}, param: {param}");
#endif            
            Profiler.End(); 
            return itemRef;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public ObjectPoolItemRef CreateTmpItem(ref P param, int valid = REF_IDLE) =>
            CreateItemRef(ref param, -1, ObjectPoolItemType.OutOfRange, valid);
        
        public ObjectPoolItemRef GetItemRef(P param, ObjectPoolItemType itemType, int valid = ValidTick)
        {
            CheckInitObjectPoolInst();
            CheckTemporaryListValid();
            
            int typeIndex = (int) itemType;
            ObjectPoolItemRef itemRef = itemType switch
            {
                ObjectPoolItemType.Temporary => GetTemporaryItem(ref param, ref m_Items[typeIndex], ref m_Indexs[typeIndex], valid),
                ObjectPoolItemType.Persistent => GetPersistentItem(ref param, ref m_Items[typeIndex], ref m_Indexs[typeIndex]),
                _ => null
            };
            
            if (null != itemRef && itemRef.InstId >= 0 && itemRef.InstId != m_Indexs[typeIndex])
                Log.Error($"ObjectPoolInst<{typeof(T)}, {typeof(P)}> GetItem Error: instId: {itemRef.InstId}, index:{m_Indexs[typeIndex]}");
            
            if (null != itemRef)
            {
                m_Indexs[typeIndex]++;
                m_Counts[typeIndex] = Math.Max(m_Indexs[typeIndex], m_Counts[typeIndex]);
            }
            else
            {
                itemRef = GetOutOfRangeItem(ref param, itemType, itemType == ObjectPoolItemType.Persistent ? REF_USED : valid);
            }
            
            if (ObjectPoolItemType.Initialization == itemRef.ItemType && null != m_Ctor)
            {
                if (null != itemRef.Item && null != m_Dispose) m_Dispose(itemRef.Item, param);
                itemRef.Item = m_Ctor(param);
                SetItemType(itemRef, itemType);
            }
            
            // Log.Info($"ObjectPoolInst GetItemRef itemCount: item: {itemRef.InstId}, index: {m_Indexs[typeIndex]}, {m_Items[typeIndex].Length}");
            return itemRef;
        }
        
        public T GetItem(ref P param, int valid)
        {
            CheckInitObjectPoolInst();
            CheckTemporaryListValid();
            
            var typeIndex = (int) ObjectPoolItemType.Temporary;
            ObjectPoolItemRef itemRef = GetTemporaryItem(ref param, ref m_Items[typeIndex], ref m_Indexs[typeIndex], valid);
            
            if (null != itemRef && itemRef.InstId >= 0 && itemRef.InstId != m_Indexs[typeIndex])
                Log.Error($"ObjectPoolInst<{typeof(T)}, {typeof(P)}> GetItem Error: instId: {itemRef.InstId}, index:{m_Indexs[typeIndex]}");
            
            if (null != itemRef)
            {
                m_Indexs[typeIndex]++;
                m_Counts[typeIndex] = Math.Max(m_Indexs[typeIndex], m_Counts[typeIndex]);
            }
            else
            {
                itemRef = GetOutOfRangeItem(ref param, ObjectPoolItemType.Temporary, valid);
            }

            if (ObjectPoolItemType.Initialization == itemRef.ItemType && null != m_Ctor)
            {
                if (null != itemRef.Item && null != m_Dispose) m_Dispose(itemRef.Item, param);
                itemRef.Item = m_Ctor(param);
                SetItemType(itemRef, ObjectPoolItemType.Temporary);
            }
            
            // Log.Info($"ObjectPoolInst GetItem: instId: {itemRef.InstId}, index:{m_Indexs[typeIndex]}. count: {m_Items[typeIndex].Length}"); 
            return itemRef.Item;
        }
        
        public T GetItem(ref P param) => GetItem(ref param, ValidTick);

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void RecycleItem(T item, ObjectPoolItemType itemType = ObjectPoolItemType.Temporary) =>
            RecycleItem(item, default, itemType);
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void RecycleItem(T item, P param, ObjectPoolItemType itemType = ObjectPoolItemType.Temporary)
        {
            Profiler.Start("ObjectPoolInst RecycleItem");
            if (Lookup.TryGetValue(item, out var itemRef))
            {
                RecycleItem(itemRef, param, itemType); 
            }
            else
            {
                RecycleOutRangeItem(new ObjectPoolItemRef(-1, -1, item, itemType), param, itemType);
            }
            Profiler.End();
        }
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void RecycleItem(ObjectPoolItemRef itemRef, ObjectPoolItemType itemType = ObjectPoolItemType.Temporary, int valid = REF_IDLE)
        {
            RecycleItem(itemRef, default, itemType, valid);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void RecycleItem(ObjectPoolItemRef itemRef, P param, ObjectPoolItemType itemType = ObjectPoolItemType.Temporary, int valid = REF_IDLE)
        {
            if (null == itemRef) return;
            Profiler.Start("ObjectPoolInst RecycleItem");
            switch (itemRef.ItemType)
            {
                case ObjectPoolItemType.Temporary:
                    if (null != SetValid) SetValid(itemRef, valid);
                    break;
                case ObjectPoolItemType.Persistent:
                    var typeIndex = (int) ObjectPoolItemType.Persistent;
                    var index = --m_Indexs[typeIndex];
                    if (itemRef.InstId != index)
                        SwapItemRef(ref m_Items[typeIndex], itemRef.InstId, index);
                    if (null != SetValid) SetValid(itemRef, valid);
                    break;
                case ObjectPoolItemType.OutOfRange:
                    RecycleOutRangeItem(itemRef, param, itemType);
                    break;
                case ObjectPoolItemType.Initialization:
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
            Profiler.End();
        }
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool RecycleOutRangeItem(ObjectPoolItemRef itemRef, P param, ObjectPoolItemType itemType)
        {
            Profiler.Start("ObjectPoolInst RecycleOutRangeItem");
            var list = itemType == ObjectPoolItemType.Temporary ? m_TemporaryList : m_PersistentList;
            var result = LookupDispose.TryGetValue(itemRef, out var disposeItem);
            SetItemType(itemRef, itemType);
            if (result)
            {
                LookupDispose.Remove(itemRef);
                if (!disposeItem.PoolInst.TryRecycleOutRangeItem(disposeItem.ItemRef))
                    disposeItem.Release(disposeItem.ItemRef.Item, disposeItem.param);
                list.Remove(disposeItem);
            }
            else
            {
                result = TryRecycleOutRangeItem(itemRef);
                if (!result)
                {
                    if (null != m_Dispose) m_Dispose(itemRef.Item, param);
                    Log.Error($"Recycle out of range item failed! item: {itemRef.Item}, itemType: {itemType}, StackTrace:{new System.Diagnostics.StackTrace()}");
                }
            }
            Profiler.End();
            return result; 
        }
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool TryRecycleOutRangeItem(ObjectPoolItemRef itemRef)
        {
            if (null == SetItemType || null == SetInstId || null == SetValid) return false;
            if (null == itemRef) return false;

            int typeIndex = (itemRef.ItemType == ObjectPoolItemType.OutOfRange)
                ? (m_Counts[0] <= m_Counts[1]
                    ? (int) ObjectPoolItemType.Temporary
                    : (int) ObjectPoolItemType.Persistent)
                : (int) itemRef.ItemType; 
            
            var itemRefs = m_Items[typeIndex];
            var index = m_Counts[typeIndex];
            
            if (index < m_MaxCount)
            {
                if (index >= itemRefs.Length)
                {
                    ArrayExtensions.CheckArraySize(ref m_Items[typeIndex], index + 1, m_MaxCount);
                    // ArrayExtensions.CheckArraySize(ref m_ItemTicks, index + 1, MaxCount);
                    itemRefs = m_Items[typeIndex];
                }

                if (index < 0 || index >= itemRefs.Length)
                {
                    Log.Error($"TryRecycleOutRangeItem index out of range! index->{index}, length->{itemRefs.Length}, max->{m_MaxCount}");
                    return false;
                }
            
                if (null == itemRefs[index])
                {
                    itemRefs[index] = itemRef;
                    SetInstId(itemRef, index);
                    SetValid(itemRef, REF_IDLE);
                    SetItemType(itemRef, (ObjectPoolItemType) typeIndex);
                }
                
                m_Counts[typeIndex]++;
                if (!Lookup.ContainsKey(itemRef.Item)) Lookup.Add(itemRef.Item, itemRef);
                return true;
            }
            Lookup.Remove(itemRef.Item);
            return false;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public ObjectPoolInst<T, P> SetTickCount(int tickCount)
        {
            TickCount = tickCount;
            return this;
        }
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public ObjectPoolInst<T, P> SetDebug(bool debug)
        {
            m_Debug = debug;
            return this;
        }
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public ObjectPoolInst<T, P> SetMaxCount(int count)
        {
            m_MaxCount = count;
            return this;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public ObjectPoolInst<T, P> SetCtor(ObjectCtor ctor)
        {
            m_Ctor = ctor;
            return this;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public ObjectPoolInst<T, P> SetDispose(ObjectDispose dispose)
        {
            m_Dispose = dispose;
            return this;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void Reset()
        {
            TickCount = REF_IDLE;
            // Array.Clear(m_ItemTicks, 0, m_ItemTicks.Length);
            // m_Index = 0;
            Array.Clear(m_Indexs, 0, m_Indexs.Length); 
            m_Ctor = null;
            m_Dispose = null;
        }
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public ObjectPoolInst<T, P> Clear()
        {
            // m_Index = 0;
            for (int i = 0; i < m_Items.Length; i++)
            {
                Array.Clear(m_Items[i], 0, m_Items[i].Length); 
            }
            Array.Clear(m_Indexs, 0, m_Indexs.Length);
            // Array.Clear(m_ItemTicks, 0, m_ItemTicks.Length);
            return this;
        }
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void FreeTemporaryList(P param)
        {
            if (null == m_Dispose) return;
            for (int i = 0; i < m_TemporaryList.Count; i++)
            {
                var item = m_TemporaryList[i];
                try
                {
                    item.Release(item.ItemRef.Item, param);
                }
                catch (Exception e)
                {
                    Log.Error(e);
                }
                finally
                {
                    Lookup.Remove(item.ItemRef.Item);
                    LookupDispose.Remove(item.ItemRef); 
                }
            }
            m_TemporaryList.Clear();
        }
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void FreePersistentList(P param)
        {
            if (null == m_Dispose) return;
            for (int i = 0; i < m_PersistentList.Count; i++)
            {
                var item = m_PersistentList[i];
                try
                {
                    item.Release(item.ItemRef.Item, param);
                }
                catch (Exception e)
                {
                    Log.Error(e);
                }
                finally
                {
                    Lookup.Remove(item.ItemRef.Item);
                    LookupDispose.Remove(item.ItemRef);
                }
            }
            m_PersistentList.Clear();
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void FreePool(P param, bool ignoreValid = false)
        {
            if (null == m_Dispose) return;
            for (int type = 0; type < m_Items.Length; type++)
            { 
                var itemRefs = m_Items[type];
                for (int i = 0; i < itemRefs.Length; i++)
                {
                    if (null != itemRefs[i])
                    {
                        try
                        {
                            if (!ignoreValid || itemRefs[i].Valid < 0)
                                m_Dispose(itemRefs[i].Item, param);
                        }
                        catch (Exception e)
                        {
                            Log.Exception(e);
                        }
                        finally
                        {
                            Lookup.Remove(itemRefs[i].Item);
                        }
                    }
                }
            }
            Clear();
        }
        
        public void Free(P param, bool ignoreValid = false)
        {
            FreeTemporaryList(param);
            FreePersistentList(param);
            FreePool(param, ignoreValid);
            if (Lookup.Count > 0) Log.Error($"Lookup count: {Lookup.Count}");
            if (LookupDispose.Count > 0) Log.Error($"LookupDispose count: {LookupDispose.Count}");
        }

        public static void CheckTemporaryListValid()
        {
            var tickCount = ObjectPoolUtil.GetTickCount();
            if (s_CheckTempTick > 0 && ObjectPoolUtil.GetTickCountDiff(tickCount, s_CheckTempTick) < 0) return;
            if (m_TemporaryList.Count > 0) Log.Info($"m_TemporaryList.Count: {m_TemporaryList.Count}");
            var index = 0;
            while (m_TemporaryList.Count > index)
            {
                var item = m_TemporaryList[index];
                // if (tickCount < item.ItemRef.Valid)
                if (!item.ItemRef.IsIdle)
                {
                    index++;
                }
                else
                {
                    m_TemporaryList.RemoveAt(index);
                    if (item.ItemRef.ItemType == ObjectPoolItemType.OutOfRange && !item.PoolInst.TryRecycleOutRangeItem(item.ItemRef))
                    {
                        LookupDispose.Remove(item.ItemRef);
                        item.Release(item.ItemRef.Item, item.param);
                    } 
                }
            }
            s_CheckTempTick = ObjectPoolUtil.GetTickCount(ValidTick);
        }

        public void DumpObjectPoolInst(string msg)
        {
            Log.Error(@$"ObjectPoolInst<{typeof(T)}> {msg}, max count: {m_MaxCount}
outofrange: [{m_TemporaryList.Count}][{m_PersistentList.Count}]
temporary: [{m_Counts[0]}][{m_Indexs[0]}]
persistent: [{m_Counts[1]}][{m_Indexs[1]}]");
        }
    }
    
    public class ObjectPoolThreadSafe<T, P>
    {
        [ThreadStatic] private static ObjectPoolInst<T, P> m_Instance;
        
        public static ObjectPoolInst<T, P> Instance
        {
            get
            {
                if (null == m_Instance)
                    m_Instance = new ObjectPoolInst<T, P>(-1);
                return m_Instance;
            }
        }
    }

    public class ObjectPoolCollection<T, P>
    {
        private const int InitCount = 2;
        private const int MaxCount = 128;

        [ThreadStatic] private static bool s_Init = false;
        [ThreadStatic] private static int s_Index;
        [ThreadStatic] private static ObjectPoolInst<T, P>[] s_IdlePools;
        [ThreadStatic] private static int TickCount;
        
        static ObjectPoolCollection()
        {
            CheckInitObjectPoolCollection();
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        static void CheckInitObjectPoolCollection()
        {
            if (s_Init) return;
            s_Index = 0;
            s_IdlePools = new ObjectPoolInst<T, P>[InitCount];
            s_Init = true;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static bool TryGetPool(int index, out ObjectPoolInst<T, P> pool)
        {
            bool result = true;
            if (index >= 0 && index < s_IdlePools.Length)
            {
                if (s_IdlePools[index] == null)
                {
                    pool = new ObjectPoolInst<T, P>(index);
                    s_IdlePools[index] = pool;
                }
                else
                {
                    pool = s_IdlePools[index];
                    if (!pool.IsIdle)
                    {
                        pool = default;
                        result = false; 
                    }
                    else
                    {
                        pool.Reset();
                        pool.SetTickCount(ObjectPoolUtil.GetTickCount(ObjectPoolInst<T, P>.ValidTick));
                    }
                }
            }
            else
            {
                pool = default;
                result = false; 
            }
            return result;
        }

        public static ObjectPoolInst<T, P> GetPool(int itemCount = ObjectPoolInst<T, P>.InitCount)
        {
            CheckInitObjectPoolCollection();
            
            Profiler.Start("ObjectPoolCollection<T, P> GetPool");
            TickCount = ObjectPoolUtil.GetTickCount();
            var index = s_Index++;
            if (s_Index >= s_IdlePools.Length) s_Index = 0;
            
            bool success = TryGetPool(index, out ObjectPoolInst<T, P> pool);
            if (!success && s_IdlePools.Length < MaxCount)
            {
                index = s_IdlePools.Length;
                ArrayExtensions.CheckArraySize(ref s_IdlePools, index + 1, MaxCount);
                success = TryGetPool(index, out pool);
            }
            
            if (!success) pool = new ObjectPoolInst<T, P>(-1, itemCount);
            // Log.Info($"ObjectPoolInst GetPool poolCount: {s_IdlePools.Length}, index: {index}, success: {success}, pool: {pool}");
            Profiler.End();
            return pool;
        }
    }
}